from DataFeeder.QueryExchTradeDates import query_trading_dates

from Misc.Utils import offset_date_by_month

from datetime import *
	
class Frequency:	
	Daily 		= 365
	Weekly 		= 52
	Monthly 	= 12
	Quarterly	= 4
	Semiannual	= 2
	Annual		= 1
	Once		= 0
	
class IntradayFrequencyUnit:
	Microsecond = 'microseconds'
	Second	= 'seconds'
	Minute	= 'minutes'
	Hour	= 'hours'
		
class DayRollingConv:
	Following = 0
	Preceding = 1

class TradingCalendar:
	
	def __init__(self, exch_list = None):
	
		if exch_list:
			trading_date_list = query_trading_dates(exch_list)
			self.min_trading_date = trading_date_list[0]
			self.max_trading_date = trading_date_list[-1]
			self.trading_date_set = set(trading_date_list)
			self.is_business_cld = True
		else:
			self.is_business_cld = False
			
		self.open_time = time(hour=9) #to be impletemented
		self.close_time = time(hour=15) #to be impletemented
		
	def is_trading_day(self, date):
	
		if self.is_business_cld:
			if date >= self.min_trading_date and date <= self.max_trading_date:
				return date in self.trading_date_set	
			else:
				print 'Warn - date exceeds trading calendar - ' + date.isoformat()
				return False
		else:
			return True
			
	def is_holiday(self, date):
		return not self.is_trading_day(date)
		
	def nearest_trading_day(self, date, rolling_conv):
	
		if self.is_business_cld:
			if date <= self.min_trading_date and rolling_conv == DayRollingConv.Preceding:
				print 'Warn - date exceeds left of trading calendar - ' + date.isoformat()
				return date
			
			if date >= self.max_trading_date and rolling_conv == DayRollingConv.Following:
				print 'Warn - date exceeds right of trading calendar - ' + date.isoformat()
				return date
			
			one_day = timedelta(days = 1)		
			nearest = date			
			while(self.is_holiday(nearest)):
				if rolling_conv == DayRollingConv.Following:
					nearest = nearest + one_day
				elif rolling_conv == DayRollingConv.Preceding:
					nearest = nearest - one_day	
				else:
					print 'Err - not defined day rolling conv - ' + rolling_conv
		
			return nearest
		else:
			return date
		
	def advance(self, ref_date, freq, rolling_conv):
	
		before_rolling = ref_date
		if freq == Frequency.Daily:
			before_rolling = ref_date + timedelta(days=1)
		elif freq == Frequency.Weekly:
			before_rolling = ref_date + timedelta(days=7)
		elif freq == Frequency.Monthly:
			before_rolling = offset_date_by_month(ref_date, 1)
		elif freq == Frequency.Quarterly:
			before_rolling = offset_date_by_month(ref_date, 3)
		elif freq == Frequency.Semiannual:
			before_rolling = offset_date_by_month(ref_date, 6)
		elif freq == Frequency.Annual:
			before_rolling = offset_date_by_month(ref_date, 12)
		elif freq == Frequency.Once:
			before_rolling = date.max
		else:
			print 'Err - not defined frequency - ' + freq

		return self.nearest_trading_day(before_rolling, rolling_conv)
		
	def offset_by_business_days(self, ref_date, num_days, forward = True):
		one_day = timedelta(days=1)	
		date = ref_date
		
		for i in range(num_days):
		
			if forward:
				date += one_day
				rolling_conv = DayRollingConv.Following
			else:
				date -= one_day
				rolling_conv = DayRollingConv.Preceding
			
			date = self.nearest_trading_day(date, rolling_conv)
			
		return date
		
	def date_series(self, start_date, end_date, freq, rolling_conv = DayRollingConv.Following):
		
		date_series = []
		d = start_date
		
		if freq == Frequency.Daily:
			i = 0
			while d <= end_date:
				if self.is_trading_day(d):
					date_series.append(d)
				i += 1
				d = start_date + i*timedelta(days=1)
		elif freq == Frequency.Weekly:
			i = 0
			while d <= end_date:
				d = self.nearest_trading_day(d, rolling_conv)
				if not (d in date_series):
					date_series.append(d)
				i += 1
				d = start_date + i*timedelta(days=7)	
		elif freq == Frequency.Monthly:
			i = 0
			while d <= end_date:
				d = self.nearest_trading_day(d, rolling_conv)
				if not (d in date_series):
					date_series.append(d)
				i += 1
				d = offset_date_by_month(start_date, i)
		elif freq == Frequency.Quarterly:
			i = 0
			while d <= end_date:
				d = self.nearest_trading_day(d, rolling_conv)
				if not (d in date_series):
					date_series.append(d)
				i += 3
				d = offset_date_by_month(start_date, i)
		elif freq == Frequency.Semiannual:
			i = 0
			while d <= end_date:
				d = self.nearest_trading_day(d, rolling_conv)
				if not (d in date_series):
					date_series.append(d)
				i += 6
				d = offset_date_by_month(start_date, i)
		elif freq == Frequency.Annual:
			i = 0
			while d <= end_date:
				d = self.nearest_trading_day(d, rolling_conv)
				if not (d in date_series):
					date_series.append(d)
				i += 12
				d = offset_date_by_month(start_date, i)
		elif freq == Frequency.Once:
			before_rolling = date.max
		else:
			print 'Err - not defined frequency - ' + freq
			
		return date_series
		
	def trading_time_series(self, start_date, end_date, freq_unit, num_of_unit):
	
		if freq_unit == IntradayFrequencyUnit.Hour:
			dt = timedelta(hours = num_of_unit)
		elif freq_unit == IntradayFrequencyUnit.Minute:
			dt = timedelta(minutes = num_of_unit)
		elif freq_unit == IntradayFrequencyUnit.Second:
			dt = timedelta(seconds = num_of_unit)
		elif freq_unit == IntradayFrequencyUnit.Microsecond:
			dt = timedelta(microseconds = num_of_unit)
		else:
			print 'Err - not defined intraday unit - ' + freq_unit
			
		dates = self.date_series(start_date, end_date, Frequency.Daily, DayRollingConv.Following)
		open_time = self.open_time
		close_time = self.close_time
		
		result = []
		
		for d in dates:
			
			open = datetime.combine(d.date(), open_time)
			close = datetime.combine(d.date(), close_time)
			
			t = open
			while t <= close:
				result.append(t)
				t += dt
				
		return result
		
		
		
		
		
		
			
		
		
		
		
			
			

